package org.nnsoft.guice.rocoto.configuration; import static org.junit.Assert.assertFalse; import static org.nnsoft.guice.rocoto.configuration.PropertiesIterator.newPropertiesIterator; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.Map.Entry; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.junit.Test; /** * Test case on {@link PropertiesIterator}. */ public final class PropertiesIteratorTestCase { /** * Test thread safety of properties iterator. * * @throws InterruptedException */ @Test public void verifyThreadSafety() throws InterruptedException { ExecutorService service = Executors.newFixedThreadPool(2); // Callable iterating over System.getProperties() Future<Void> future = service.submit(new Callable<Void>() { public Void call() throws Exception { try { while (true) { Iterator<Entry<String, String>> it = newPropertiesIterator(System.getProperties()); while (it.hasNext()) { it.next(); } Thread.sleep(10L); } } catch (InterruptedException e) { return null; } } }); // Runnable updating System.getProperties() service.execute(new Runnable() { public void run() { while (true) { String key = "new key " + System.nanoTime(); System.getProperties().setProperty(key, "test"); try { Thread.sleep(10L); } catch (InterruptedException e) { return; } System.getProperties().remove(key); } } }); // Process for 2 seconds Thread.sleep(2000L); service.shutdownNow(); try { future.get(); } catch (ExecutionException e) { // If cause of execution failure is ConcurrentModificationException, iterator is not thread safe assertFalse(e.getCause() instanceof ConcurrentModificationException); } } }